#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <iostream>
#include <iomanip>
#include <fstream>
#include <vector>
#include <string>
#include <getopt.h>
//#include <verilated.h>
#include "VTestHarness.h"

#ifdef SOC_VCD
#include <verilated_vcd_c.h>	// Trace file format header
#endif

using namespace std;
extern "C" void flash_init(char *img);
extern bool isFinish();
static uint64_t main_cycles;

class SocEmu
{
private:
   char* flash_image;
   //VysyxSoCFull* dut_ptr;
   VTestHarness* dut_ptr;
#ifdef SOC_VCD
   VerilatedVcdC* tfp;
#endif
   uint64_t max_cycles, cycles;
   uint64_t main_times;

   static const struct option long_options[];
   static void print_help(const char *file);

public:
   SocEmu(int argc, const char *argv[]):
   dut_ptr(new VTestHarness),
   flash_image(nullptr),
   max_cycles(-1),cycles(0),main_times(0)
   {
       auto args = parse_args(argc, argv);

       // init bin to flash

       //extern  void _flash_init(char *img);
       flash_init(flash_image);

   }
   ~SocEmu();
   vector<const char *> parse_args(int argc, const char *argv[]);

   void reset_n(uint64_t n){
       while(n--) {
         dut_ptr->reset = 1;
         dut_ptr->clock = 0;
         dut_ptr->eval();
#ifdef SOC_VCD
         tfp->dump(main_times++);
#endif
         dut_ptr->clock = 1;
         dut_ptr->eval();
         dut_ptr->reset = 0;
#ifdef SOC_VCD
         tfp->dump(main_times++);
#endif
       }
   }

   void single_cycle() {
       dut_ptr->clock = 0;
       dut_ptr->eval();
//#ifdef SOC_VCD
//       tfp->dump(main_times++);
//#endif
       dut_ptr->clock = 1;
       dut_ptr->eval();
#ifdef SOC_VCD
        if(this->cycles > 5700000)
            tfp->dump(main_times++);
#endif
      this->cycles++;
      main_cycles++;
   }

    void execute() {

#ifdef SOC_VCD
        Verilated::traceEverOn(true);	// Verilator must compute traced signals
        VL_PRINTF("Enabling SoC waves...\n");
        tfp = new VerilatedVcdC;
        dut_ptr->trace(tfp, 99);	// Trace 99 levels of hierarchy
        tfp->open("build/soc/soc_dump.vcd");	// Open the dump file
#endif
        printf("\t SoC Emulation \t\n");
        reset_n(10);
        while (!isFinish())
        {
            single_cycle();
        }
#ifdef SOC_VCD
        tfp->close();
#endif
    }
};

